package org.chen.service.impl;

import co.elastic.clients.elasticsearch._types.FieldSort;
import co.elastic.clients.elasticsearch._types.SortOptions;
import co.elastic.clients.elasticsearch._types.SortOrder;
import co.elastic.clients.elasticsearch._types.query_dsl.BoolQuery;
import co.elastic.clients.elasticsearch._types.query_dsl.MultiMatchQuery;
import co.elastic.clients.elasticsearch._types.query_dsl.TermQuery;
import co.elastic.clients.elasticsearch.core.*;
import co.elastic.clients.elasticsearch.indices.RefreshRequest;
import co.elastic.clients.util.ObjectBuilder;
import com.mongodb.client.result.DeleteResult;
import com.mongodb.client.result.UpdateResult;
import lombok.extern.slf4j.Slf4j;
import org.chen.aop.ElasticsearchRefreshAspect;
import org.chen.aop.RefreshES;
import org.chen.exception.NewsException;
import org.chen.mapper.ArticleElasticsearchRepository;
import org.chen.mapper.ArticleRepository;
import org.chen.model.dto.ArticleDTO;
import org.chen.model.dto.ArticleUpdateDTO;
import org.chen.model.elastic.AdminArticleQueryParam;
import org.chen.model.elastic.ArticleIndex;
import org.chen.model.elastic.ArticlePageResult;
import org.chen.model.mongodb.ArticleDocument;
import org.chen.model.vo.ArticleVO;
import org.chen.service.IArticleService;
import org.chen.utils.ArticleConverter;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.function.Function;
import java.util.stream.Collectors;

import co.elastic.clients.elasticsearch.ElasticsearchClient;

@Service
@Slf4j
public class AdminArticleServiceImpl implements IArticleService {

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    private final ArticleRepository articleDocumentRepository;
    private final ArticleElasticsearchRepository articleIndexRepository;
    private final MongoTemplate mongoTemplate;
    private final ElasticsearchClient esClient;
    private final TagManagementService tagManagementService;
    public static final Integer STATUS = -1;
    private final static String ARTICLE_CACHE = "articleCache";

    public AdminArticleServiceImpl(ArticleRepository articleDocumentRepository,
                              ArticleElasticsearchRepository articleIndexRepository,
                              MongoTemplate mongoTemplate,
                              ElasticsearchClient esClient,
                              TagManagementService tagManagementService) {
        this.articleDocumentRepository = articleDocumentRepository;
        this.articleIndexRepository = articleIndexRepository;
        this.mongoTemplate = mongoTemplate;
        this.esClient = esClient;
        this.tagManagementService = tagManagementService;
    }

    @Override
    public ArticlePageResult getArticleList(AdminArticleQueryParam queryParam) {
        try {
            // 1. 构建查询条件
            BoolQuery.Builder boolQuery = new BoolQuery.Builder();

            // 2. 添加查询条件
            // 关键词搜索
            if (StringUtils.hasText(queryParam.getKeyword())) {
                boolQuery.must(new MultiMatchQuery.Builder()
                        .query(queryParam.getKeyword())
                        .fields("title^2", "author^1.5", "category", "tags")
                        .build()._toQuery());
            }

            // 来源筛选
            boolQuery.must(new TermQuery.Builder()
                    .field("source")
                    .value("admin")
                    .build()._toQuery());

            // 状态筛选
            if (queryParam.getStatus() != null) {
                if (!STATUS.equals(queryParam.getStatus())) {
                    boolQuery.must(new TermQuery.Builder()
                            .field("status")
                            .value(queryParam.getStatus())
                            .build()._toQuery());
                }else {
                    boolQuery.mustNot(new TermQuery.Builder()
                            .field("status")
                            .value(0)
                            .build()._toQuery());
                }
            }

            // 3. 构建排序
            List<SortOptions> sortOptions = new ArrayList<>();

            // 置顶优先
            sortOptions.add(new SortOptions.Builder()
                    .field(new FieldSort.Builder()
                            .field("isTop")
                            .order(SortOrder.Desc)
                            .build())
                    .build());

            // 自定义排序
            if (StringUtils.hasText(queryParam.getSortField())) {
                SortOrder sortOrder = "desc".equalsIgnoreCase(queryParam.getSortOrder()) ?
                        SortOrder.Desc : SortOrder.Asc;
                sortOptions.add(new SortOptions.Builder()
                        .field(new FieldSort.Builder()
                                .field(queryParam.getSortField())
                                .order(sortOrder)
                                .build())
                        .build());
            } else {
                // 默认按创建时间倒序
                sortOptions.add(new SortOptions.Builder()
                        .field(new FieldSort.Builder()
                                .field("createTime")
                                .order(SortOrder.Desc)
                                .build())
                        .build());
            }

            // 4. 构建分页
            int page = Math.max(queryParam.getPage() - 1, 0);
            int size = queryParam.getSize() != null ? queryParam.getSize() : 10;

            // 5. 执行查询
            SearchResponse<ArticleIndex> response = esClient.search(s -> s
                            .index("articles")
                            .query(boolQuery.build()._toQuery())
                            .sort(sortOptions)
                            .from(page * size)
                            .size(size),
                    ArticleIndex.class);

            // 6. 处理结果
            List<ArticleVO> articles = response.hits().hits().stream()
                    .map(hit -> ArticleConverter.toVOFromIndex(hit.source()))
                    .collect(Collectors.toList());

            // 7. 构建返回结果
            assert response.hits().total() != null;
            return ArticlePageResult.builder()
                    .records(articles)
                    .total(response.hits().total().value())
                    .page(queryParam.getPage())
                    .size(size)
                    .build();

        } catch (Exception e) {
            log.error("查询文章列表失败", e);
            throw new RuntimeException("查询文章列表失败", e);
        }
    }

    @Override
    public ArticlePageResult getDraftsArticleList(AdminArticleQueryParam queryParam) {
        try {
            // 1. 构建查询条件
            BoolQuery.Builder boolQuery = new BoolQuery.Builder();

            // 2. 添加查询条件
            // 关键词搜索
            if (StringUtils.hasText(queryParam.getKeyword())) {
                boolQuery.must(new MultiMatchQuery.Builder()
                        .query(queryParam.getKeyword())
                        .fields("title^2", "author^1.5", "category", "tags")
                        .build()._toQuery());
            }

            // 状态筛选
            boolQuery.must(new TermQuery.Builder()
                    .field("status")
                    .value(0)
                    .build()._toQuery());

            // 3. 构建排序
            List<SortOptions> sortOptions = new ArrayList<>();

            // 置顶优先
            sortOptions.add(new SortOptions.Builder()
                    .field(new FieldSort.Builder()
                            .field("isTop")
                            .order(SortOrder.Desc)
                            .build())
                    .build());

            // 自定义排序
            if (StringUtils.hasText(queryParam.getSortField())) {
                SortOrder sortOrder = "desc".equalsIgnoreCase(queryParam.getSortOrder()) ?
                        SortOrder.Desc : SortOrder.Asc;
                sortOptions.add(new SortOptions.Builder()
                        .field(new FieldSort.Builder()
                                .field(queryParam.getSortField())
                                .order(sortOrder)
                                .build())
                        .build());
            } else {
                // 默认按创建时间倒序
                sortOptions.add(new SortOptions.Builder()
                        .field(new FieldSort.Builder()
                                .field("createTime")
                                .order(SortOrder.Desc)
                                .build())
                        .build());
            }

            // 4. 构建分页
            int page = Math.max(queryParam.getPage() - 1, 0);
            int size = queryParam.getSize() != null ? queryParam.getSize() : 10;

            // 5. 执行查询
            SearchResponse<ArticleIndex> response = esClient.search(s -> s
                            .index("articles")
                            .query(boolQuery.build()._toQuery())
                            .sort(sortOptions)
                            .from(page * size)
                            .size(size),
                    ArticleIndex.class);

            // 6. 处理结果
            List<ArticleVO> articles = response.hits().hits().stream()
                    .map(hit -> ArticleConverter.toVOFromIndex(hit.source()))
                    .collect(Collectors.toList());

            // 7. 构建返回结果
            assert response.hits().total() != null;
            return ArticlePageResult.builder()
                    .records(articles)
                    .total(response.hits().total().value())
                    .page(queryParam.getPage())
                    .size(size)
                    .build();

        } catch (Exception e) {
            log.error("查询文章列表失败", e);
            throw new RuntimeException("查询文章列表失败", e);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    @RefreshES
    public void updateArticle(String id, ArticleUpdateDTO articleDTO) {
        // 1. 获取现有文档
        ArticleDocument document = articleDocumentRepository.findById(id)
                .orElseThrow(() -> new RuntimeException("文章不存在"));

        // 2. 更新文档
        updateDocument(document, articleDTO);

        // 3. 保存到MongoDB
        document = articleDocumentRepository.save(document);

        // 4. 更新ES索引
        ArticleIndex index = ArticleConverter.toIndex(document);
        articleIndexRepository.save(index);



    }

    private void updateDocument(ArticleDocument document, ArticleUpdateDTO articleDTO) {
        // 更新基本信息
        document.setTitle(articleDTO.getTitle());
        document.setContentHtml(articleDTO.getContentHtml());
        document.setCategory(articleDTO.getCategory());
        document.setUpdateTime(new Date());

        // 处理标签
        List<String> tags = tagManagementService.processArticleTags(document, articleDTO.getTags());
        document.setTags(tags);

        // 更新图片相关
        if (StringUtils.hasText(articleDTO.getThumbnailUrl())) {
            document.setThumbnailUrl(articleDTO.getThumbnailUrl());
        }
    }

    @Override
    @Transactional
    public void deleteArticle(String id) {
        // 1. 从MongoDB删除
        articleDocumentRepository.deleteById(id);

        // 2. 从Elasticsearch删除
        articleIndexRepository.deleteById(id);
    }

    @Override
    @Transactional
    public void updateStatus(String id, Integer status) {
        // 1. 更新MongoDB状态
        ArticleDocument document = articleDocumentRepository.findById(id)
                .orElseThrow(() -> new RuntimeException("文章不存在"));

        document.setStatus(status);
        document.setUpdateTime(new Date());
        articleDocumentRepository.save(document);

        // 2. 更新Elasticsearch状态
        ArticleIndex index = articleIndexRepository.findById(id)
                .orElseThrow(() -> new RuntimeException("文章索引不存在"));

        index.setStatus(status);
        articleIndexRepository.save(index);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    @RefreshES
    public String createArticle(ArticleDTO articleDTO) {
        ArticleDocument document = new ArticleDocument();
        if (articleDTO.getId() == null || articleDTO.getId().isEmpty()) {
            // 1. 创建MongoDB文档
            document.setSource("admin");
            document.setAuthor("admin");
            document.setViewCount(0L);
            document.setCreateTime(new Date());
        }else {
            // 更新文章，先获取原文档
            document = mongoTemplate.findById(articleDTO.getId(), ArticleDocument.class);
            if (document == null) {
                throw new NewsException("文章不存在");
            }
        }

        // 更新文档属性
        document.setTitle(articleDTO.getTitle());
        document.setContentHtml(articleDTO.getContentHtml());
        document.setSummary(articleDTO.getSummary());
        document.setCategory(articleDTO.getCategory());
        document.setThumbnailUrl(articleDTO.getThumbnailUrl());
        document.setStatus(articleDTO.getStatus() != null ? articleDTO.getStatus() : 0);
        document.setUpdateTime(new Date());
        document.setIsHot(false);
        document.setIsRecommend(false);
        document.setIsTop(false);

        // 处理标签
        List<String> tags = tagManagementService.processArticleTags(document, articleDTO.getTags());
        document.setTags(tags);

        // 保存到MongoDB
        document = mongoTemplate.save(document);

        // 保存到Elasticsearch
        try {
            final String id = document.getId();
            ArticleIndex index = ArticleConverter.toIndex(document);
            UpdateRequest<ArticleIndex, ArticleIndex> request = UpdateRequest.of(u -> u
                    .index("articles")
                    .id(id)
                    .doc(index)
                    .docAsUpsert(true)  // 如果文档不存在则创建
            );
            esClient.update(request, ArticleIndex.class);

        } catch (Exception e) {
            log.error("Failed to save article to elasticsearch", e);
            throw new RuntimeException("保存到ES失败", e);
        }

        return document.getId();
    }

    @Override
    @Transactional
    public ArticleVO getArticleById(String id) {
        // 1. 从MongoDB获取完整文档
        ArticleDocument document = articleDocumentRepository.findById(id)
                .orElseThrow(() -> new RuntimeException("文章不存在"));

        // 2. 转换为VO
        return ArticleConverter.toVO(document);
    }

    @Override
    @Transactional
    public ArticleVO getArticleByIdForEdit(String id) {
        // 1. 从MongoDB获取完整文档
        ArticleDocument document = articleDocumentRepository.findById(id)
                .orElseThrow(() -> new RuntimeException("文章不存在"));

        // 2. 转换为VO
        return ArticleConverter.toVOForEdit(document);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    @RefreshES
    public void updateArticlesSource(List<String> ids, String source) {
        try {
            // 1. 批量更新MongoDB
            Query query = Query.query(Criteria.where("id").in(ids));
            Update update = Update.update("source", source)
                    .set("updateTime", new Date());

            UpdateResult mongoResult = mongoTemplate.updateMulti(
                    query, update, ArticleDocument.class);

            log.info("Updated {} documents in MongoDB", mongoResult.getModifiedCount());

            // 2. 批量更新ES
            BulkRequest.Builder br = new BulkRequest.Builder();
            for (String id : ids) {
                br.operations(op -> op
                        .update(u -> u
                                .index("articles")
                                .id(id)
                                .action(a -> a
                                        .doc(Map.of(
                                                "source", source,
                                                "updateTime", new Date()
                                        ))
                                )
                        )
                );
            }

            BulkResponse result = esClient.bulk(br.build());
            if (result.errors()) {
                log.error("Error updating documents in Elasticsearch");
                throw new RuntimeException("Failed to update articles in Elasticsearch");
            }

        } catch (Exception e) {
            log.error("Error updating articles source", e);
            throw new RuntimeException("Failed to update articles source", e);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    @RefreshES
    public void updateArticlesStatus(List<String> ids, Integer status) {
        try {
            // 1. 批量更新MongoDB
            Query query = Query.query(Criteria.where("id").in(ids));
            Update update = Update.update("status", status)
                    .set("updateTime", new Date());

            UpdateResult mongoResult = mongoTemplate.updateMulti(
                    query, update, ArticleDocument.class);

            log.info("Updated {} documents in MongoDB", mongoResult.getModifiedCount());

            // 2. 批量更新ES
            BulkRequest.Builder br = new BulkRequest.Builder();
            for (String id : ids) {
                br.operations(op -> op
                        .update(u -> u
                                .index("articles")
                                .id(id)
                                .action(a -> a
                                        .doc(Map.of(
                                                "status", status,
                                                "updateTime", new Date()
                                        ))
                                )
                        )
                );
            }

            BulkResponse result = esClient.bulk(br.build());
            if (result.errors()) {
                log.error("Error updating documents in Elasticsearch");
                throw new RuntimeException("Failed to update articles in Elasticsearch");
            }

        } catch (Exception e) {
            log.error("Error updating articles status", e);
            throw new RuntimeException("Failed to update articles status", e);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    @RefreshES
    public void deleteArticles(List<String> ids) {
        try {
            // 1. 批量删除MongoDB文档
            Query query = Query.query(Criteria.where("id").in(ids));
            DeleteResult mongoResult = mongoTemplate.remove(query, ArticleDocument.class);

            log.info("Deleted {} documents from MongoDB", mongoResult.getDeletedCount());

            // 2. 批量删除ES文档
            BulkRequest.Builder br = new BulkRequest.Builder();
            for (String id : ids) {
                br.operations(op -> op
                        .delete(d -> d
                                .index("articles")
                                .id(id)
                        )
                );
            }

            BulkResponse result = esClient.bulk(br.build());
            if (result.errors()) {
                log.error("Error deleting documents from Elasticsearch");
                throw new RuntimeException("Failed to delete articles from Elasticsearch");
            }

        } catch (Exception e) {
            log.error("Error deleting articles", e);
            throw new RuntimeException("Failed to delete articles", e);
        }
    }
}
